package com.ext.replacedemo;

import android.text.TextUtils;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.Closeable;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.Formatter;
import java.util.List;

/**
 * Created by FengLing at
 */
public class MigrateV_X {
    /* true-允许转换，false-仅作检查 */
    private boolean mMigrate = false;
    /* true-允许转换为34                  */
    private boolean mMigrateTo34 = false;
    /* true-允许转换为35                  */
    private boolean mMigrateTo35 = false;
    /* true-允许转换AndroidX             */
    private boolean mMigrateToX = false;
    /* true-删除Facebook              */
    private boolean mRemoveFacebook = false;
    /* true-跳过特定行和被注释的行      */
    private boolean mIgnoredLine = false;
    /* true-显示全部日志                */
    private boolean mShowAllLog = false;
    /*true-允许删除特定行 “公库上传到内网服务器maven仓库” 以下的所有内容 */
    private boolean mNeedDelMavenUpload = true;
    /*替換后需要被立即删除的字符串*/
    private String[] mNeedRemoveAfterReplaceStr = {");//删--"};

    //<editor-fold desc="fixed properties">
    private ArrayList<String> mGradleKeyList;
    private ArrayList<String> mGradleValueList;
    private ArrayList<String> mGradleKeyLineList;
    private ArrayList<String> mGradleValueLineList;

    private ArrayList<String> mIgnoredKeyList;
    private ArrayList<String> mIgnoredValueList;

    private ArrayList<String> mJXKeyList;
    private ArrayList<String> mJXValueList;
    private ArrayList<String> mJXKeyLineList;
    private ArrayList<String> mJXValueLineList;

    /*根据尾缀来识别是否为目标文件 */
    private ArrayList<String> mNameSuffix = new ArrayList<>();
    /*日志*/
    private File mLogFile;
    private PrintWriter mLogPr;
    private ArrayList<String> mLogList = new ArrayList<>();
    /*统计修改*/
    private boolean mModified;
    private int mModifiedCount;
    private int mModifiedFileCount;
    /*最终写入文件的文本*/
    private StringBuilder mSb = new StringBuilder();
    /*格式化输出*/
    private StringBuilder tmpSb = new StringBuilder();
    private Formatter sF = new Formatter(tmpSb);
    /*特定路径：根目录下的build.gradle*/
    private String rootBuildGradlePath;

    //</editor-fold>

    //<editor-fold desc="set">
    public MigrateV_X setMigrate(boolean migrate) {
        mMigrate = migrate;
        return this;
    }

    public MigrateV_X setMigrateTo34(boolean migrateTo34) {
        mMigrateTo34 = migrateTo34;
        return this;
    }

    public MigrateV_X setMigrateTo35(boolean migrateTo35) {
        mMigrateTo35 = migrateTo35;
        return this;
    }

    public MigrateV_X setMigrateToX(boolean migrateToX) {
        mMigrateToX = migrateToX;
        return this;
    }

    public MigrateV_X setRemoveFacebook(boolean removeFacebook) {
        mRemoveFacebook = removeFacebook;
        return this;
    }

    public MigrateV_X setNeedIgnoredLine(boolean needIgnoredLine) {
        mIgnoredLine = needIgnoredLine;
        return this;
    }

    public MigrateV_X setShowAllLog(boolean showAllLog) {
        mShowAllLog = showAllLog;
        return this;
    }
    //</editor-fold>

    public void doMigrate(File mRootPath, List<String> suffixList) throws Throwable {
        if (!mRootPath.exists()) {
            throw new IllegalArgumentException("File path is Null...");
        }

        mLogFile = new File("src\\test", "Log.txt");
        System.out.println("Log: " + mLogFile.getAbsolutePath());

        loadKVCfgList(mMigrateTo34, mMigrateTo35, mMigrateToX, mRemoveFacebook);
        mNameSuffix.addAll(suffixList);

        mLogPr = new PrintWriter(new FileWriter(mLogFile, false));
        printLog(false, "可搜索日志关键字：忽略__、替换__，替换____，异常_，目标文件尾缀：" + mNameSuffix);

        if (mRootPath.isFile()) {
            rootBuildGradlePath = mRootPath.getParent() + "\\build.gradle";
        } else {
            rootBuildGradlePath = mRootPath.getAbsolutePath() + "\\build.gradle";
        }

        try {
            int length = listAll(mRootPath);

            String msg = mMigrate ? "\n\n---------------------检查下方---------------------"
                    : "\n\n---------------------当前仅作检查---------------------(請先刪除Log.txt，当前已改为仅检测)";
            msg += "\n    android.enableJetifier=true" +
                    "\n    檢查：ACCESS_BACKGROUND_LOCATION, EventBus 3.1.1" +
                    "\n    api 'com.daimajia.easing:library:2.0@aar'" +
                    "\n    api 'com.daimajia.androidanimations:library:2.3@aar'" +
                    "\n";
            printLog(false, msg);

            msg = "\n可搜索日志关键字：忽略__，替换__，替换____，异常_，改动行数：" + mModifiedCount + "，改动文件：" + mModifiedFileCount + "，总累计遍历文件数：" + length;
            printLog(false, msg);

            msg = "\n\nGradle  载入：" + mGradleKeyList.size() + ",  Gradle  Line载入：" + mGradleKeyLineList.size();
            msg += "\nJavaXml 载入：" + mJXKeyList.size() + ", JavaXml Line载入：" + mJXKeyLineList.size();
            printLog(false, msg);

            saveLogToFile();
        } finally {
            closeIO(mLogPr);
        }
    }

    private int listAll(File file) throws Throwable {
        if (file.isFile()) {
            onFile(file, file.getName(), file.getAbsolutePath());
            return 1;
        }
        File[] files = file.listFiles();

        if (files != null && files.length > 0) {
            int allFileSize = files.length;
            for (File f : files) {
                String absolutePath = f.getAbsolutePath();
                String fileName = f.getName();
                if (f.isDirectory()) {
                    if (fileName.equals(".gradle") || fileName.equals(".idea")
                            || fileName.equals(".git") || fileName.equals("build")
                            || fileName.endsWith("---")) {
                        if (mShowAllLog) {
                            printLog(true, "\n---ignored--->" + absolutePath);
                        }
                        continue;
                    }
                    allFileSize += listAll(f);
                } else {
                    onFile(f, fileName, absolutePath);
                }
            }
            return allFileSize;
        } else {
            return 0;
        }
    }

    private void onFile(File f, String fileName, String absolutePath) throws IOException {
        if (isTargetFile(fileName)) {
            printLog(false, "\n当前：" + absolutePath);

            mModified = false; /* true-> 修改成功 */
            readAndModifyFile(f, absolutePath);

            if (!mModified) {
                mLogList.remove(mLogList.size() - 1);
//                printLog(false, "\n文件实际未改动");
            }
        }
    }

    boolean delMavenUpload;
    boolean delCardAppDep;

    private void readAndModifyFile(File f, String absolutePath) throws IOException {
        BufferedReader br = null;
        BufferedWriter bw = null;
        mSb.setLength(0);

        boolean isRootGradle = absolutePath.equals(rootBuildGradlePath);
        boolean isGradle = absolutePath.endsWith(".gradle") || absolutePath.endsWith("gradle-wrapper.properties");

        delMavenUpload = false;/*状态：是否正删除特定行以下的所有内容*/

        int lineNum = 0;
        try {
            br = new BufferedReader(new FileReader(f));
            String line;
            while (true) {
                line = br.readLine();
                if (line == null) {
                    break;
                }
                lineNum++;

//                if (mShowAllLog) {
//                    String format = "\n    %04d 【%s】";
//                    printLogWithLineNum(format, lineNum, line);
//                }
                if (line.contains("getClipboardChanged")) {
                    printLogWithLineNum("\n    %04d 【%s】\n 全局搜索__【%s】", lineNum, line, "");
                    continue;
                }
                boolean isModified;
                if (isGradle) {
                    isModified = handleGradleLine(isRootGradle, line, lineNum, f);
                } else {
                    isModified = handleLine(line, lineNum);
                }
                if (isModified) {
                    mModifiedCount++;
                    if (!mModified) {
                        mModified = true;
                        mModifiedFileCount++;
                    }
                } else {
                    if (lineNum > 1) {
                        mSb.append("\n");
                    }
                    mSb.append(line);
                }
            }
        } finally {
            closeIO(br);
        }
        if (delMavenUpload) {
            printLog(false, "\n    -----------------存在待删除的maven上传代码-----------------");
        }

        if (mMigrate && mModified) {
            String sbStr = mSb.toString();
            try {
                bw = new BufferedWriter(new FileWriter(f, false));
                bw.write(sbStr);
            } finally {
                closeIO(bw);
            }
        }
    }

    private boolean handleGradleLine(boolean isRootGradle, String line, int lineNum, File f) {
        if (mMigrateTo34 && isRootGradle) {
            Boolean ret = onInterceptRootGradleLine(line, lineNum);
            if (ret != null) {
                return ret;
            }
        }
        if (mNeedDelMavenUpload) {
            if (delMavenUpload || (delMavenUpload = line.contains("公库上传到内网服务器"))) {
                return true;
            }
        }

        boolean isModified = onInterceptGradleLine(line, lineNum, f);
        if (isModified) {
            return true;
        }

        if (toReplace(true, mGradleKeyList, mGradleValueList, line, lineNum)) {
            return true;
        }
        if (needIgnored(line, lineNum)) {
            return false;
        }
        if (toReplace(false, mGradleKeyLineList, mGradleValueLineList, line, lineNum)) {/*整行替换*/
            return true;
        }

        return false;
    }

    private Boolean onInterceptRootGradleLine(String line, int lineNum) {
        boolean okLine = line.endsWith("/*ok*/");
        if (line.contains("    repositories {") && !okLine) {
            printLog(false, "\n    ---------------------添加了5行 maven url");
            mSb.append("\n").append(line).append("/*ok*/");
            mSb.append("\n        maven { url 'https://maven.aliyun.com/repository/google' }");
            mSb.append("\n        maven { url 'https://maven.aliyun.com/repository/public' }");
            mSb.append("\n        maven { url 'https://maven.aliyun.com/repository/gradle-plugin' }");
            mSb.append("\n        maven { url 'https://jitpack.io' }");
            mSb.append("\n        maven { url 'https://developer.huawei.com/repo/' }");
            return true;
            //            } else if (okLine && mShowAllLog) {
            //                printLog(false, "\n          忽略__：该行已处理，保持原样" + line);
        }
        if (line.contains("    dependencies {")) {
            return false;/* 忽略根gradle的这个节点，不走后续的处理流程*/
        }
        return null;/*表示可以走后续的处理流程*/
    }

    /*如果能不修改一定不要修改，且一定要返回false，主流程会根据返回值为false时，自动append当前行*/
    private boolean onInterceptGradleLine(String line, int lineNum, File f) {
        if (needIgnored(line, lineNum)) {
            return false;
        }
        String noEmptyLine = line.replace(" ", "");
        if (mMigrateTo35) {
            if (noEmptyLine.contains("defaultConfig{")) {
                File manifestFile = new File(f.getParentFile(), "/src/main/AndroidManifest.xml");
                if (manifestFile.exists()) {
                    String namespace = getNameSpaceFromManifest(manifestFile);
                    if (namespace == null || namespace.trim().length() == 0) {
                        printLogWithLineNum("\n    %04d 【%-80s】 异常_ 获取失败：", lineNum, line);
                        return false;
                    }

                    mSb.append("\n    namespace \"").append(namespace).append("\"");
                    mSb.append("\n").append(line).append("/*ok*/");
                    return true;
                } else {
                    printLogWithLineNum("\n    %04d 【%-80s】 异常_ 没有清单文件", lineNum, line);
                    return false;
                }
            } else if (noEmptyLine.contains("@Bind(")) {
//                if (mButterKnifeToViewBinding) {
//                    String str = noEmptyLine.substring(6, noEmptyLine.length());
//                    mBindList.add("viewbing." + str);
//                }
            }
        } else if (mMigrateToX) {
            if ((noEmptyLine.startsWith("implementation") || noEmptyLine.startsWith("api"))) {

                if (line.contains("com.cardapp.")) {
                    if (line.contains("mobile_printer") || line.contains("apkdownload")) {
                        mSb.append("\n// ").append(line);/*保留原行方便查看*/
                    } else {
                        String str = line.split(":")[1].replace(" ", "").replace("-", "");
                        mSb.append("\n    api cardappDependencies.").append(str).append("X");
                        mSb.append("\n// ").append(line);/*保留原行方便查看*/
                    }
                    printLogWithLineNum("\n    %04d 【%-80s】 拦截    替换__：", lineNum, line);
//                    mRemoveList.add(
                    if (line.contains(") {") || line.contains("){")) {
                        delCardAppDep = true;
                    }
                    return true;
                } else {
                    if (line.contains("cardappDependencies.") && !line.endsWith("X")) {
                        mSb.append("\n").append(line).append("X");
                        return true;
                    }
                }
            }
        }
        if (delCardAppDep) {
            if (noEmptyLine.equals("}")) {
                delCardAppDep = false;
            }
            mSb.append("\n//").append(line);
            printLogWithLineNum("\n    %04d 【%-80s】 拦截后续 替换__：", lineNum, line);
            return true;
        }
        return false;
    }

    private boolean handleLine(String line, int lineNum) {
        if (toReplace(true, mJXKeyList, mJXValueList, line, lineNum)) {
            return true;
        }
        if (needIgnored(line, lineNum)) {
            return false;
        }
        if (toReplace(false, mJXKeyLineList, mJXValueLineList, line, lineNum)) {/*整行替换*/
            return true;
        }
        return false;
    }

    private boolean needIgnored(String line, int lineNum) {
        if (!mIgnoredLine) {
            return false;
        }
        String v = existIgnoredRule(line);
        if (v != null) {
            if (mShowAllLog) {
                printLogWithLineNum("\n    %04d 【%s】\n        忽略__：符合规则:%s", lineNum, line, v);
            }
            return true;
        }
        String noEmptyLine = line.replace(" ", "");
        v = existIgnoredRule(noEmptyLine);
        if (v != null) {
            if (mShowAllLog) {
                printLogWithLineNum("\n    %04d 【%s】\n        忽略__：符合规则:%s", lineNum, line, v);
            }
            return true;
        }

        if ((noEmptyLine.startsWith("/*") && noEmptyLine.endsWith("*/"))
                || (noEmptyLine.startsWith("/*") && !noEmptyLine.contains("*/"))
                || (!noEmptyLine.contains("/*") && noEmptyLine.endsWith("*/"))
                || (!noEmptyLine.contains("/*") && noEmptyLine.contains("*///"))
                || (noEmptyLine.startsWith("<!--") && noEmptyLine.endsWith("-->"))) {
            if (mShowAllLog) {
                printLogWithLineNum("\n    %04d 【%s】\n        忽略__：是注释行", lineNum, line);
            }
            return true;
        }
        return false;
    }

    private String existIgnoredRule(String str) {
        int size = mIgnoredKeyList.size();
        for (int i = 0; i < size; i++) {
            String key = mIgnoredKeyList.get(i);
            String value = mIgnoredValueList.get(i);

            if ("startsWith".equals(key)) {
                boolean ret = str.startsWith(value);
                if (ret) {
                    return value;
                }
            } else if ("contains".equals(key)) {
                boolean ret = str.contains(value);
                if (ret) {
                    return value;
                }
            } else if ("endsWith".equals(key)) {
                boolean ret = str.endsWith(value);
                if (ret) {
                    return value;
                }
            } else if ("equals".equals(key)) {
                boolean ret = str.equals(value);
                if (ret) {
                    return value;
                }
            }
        }
        return null;
    }

    private boolean toReplace(boolean replacePart, ArrayList<String> keyList, ArrayList<String> valueList,
                              String line, int lineNum) {

        for (int i = 0; i < keyList.size(); i++) {
            String key = keyList.get(i);
            if (!line.contains(key)) {
                continue;
            }
            String replacement = valueList.get(i);

            String replacedStr;
            if (replacePart) {
                replacedStr = line.replace(key, replacement);

                checkResult(line, lineNum, replacedStr, key);

                printLogWithLineNum("\n    %04d 【%s】\n 局部替换__【%s】", lineNum, line, replacedStr);
            } else {
                if (line.equals(replacement.replace("{0}", line))) {
                    if (mShowAllLog) {
                        printLogWithLineNum("\n    %04d 【%s】\n        忽略__：内容已相等", lineNum, line);
                    }
                    return false;
                }

                if (replacement.contains("{0}")) {
                    replacedStr = replacement.replace("{0}", line).concat(" /*ok*/");
                } else {
                    replacedStr = replacement;
                }

                printLogWithLineNum("\n    %04d 【%s】  存在【%s】\n 整行替换__【%s】", lineNum, line, key, replacedStr);
            }
            if (mNeedRemoveAfterReplaceStr != null) {
                for (String removeStr : mNeedRemoveAfterReplaceStr) {
                    replacedStr = replacedStr.replace(removeStr, "");
                }
            }

            if (!"".equals(replacedStr)) {
                mSb.append("\n").append(replacedStr);
            }
            return true;
        }
        return false;
    }

    private void checkResult(String line, int lineNum, String replacedStr, String key) {
        if (needIgnored(replacedStr, lineNum)) {
            return;
        }
        if (replacedStr.contains(key)) {
            printLog(false, "替换____" + line + "----【" + replacedStr);
        }
    }

    private void loadKVCfgList(boolean migrateTo34, boolean migrateTo35, boolean migrateToX, boolean removeFacebook) {
        Gradle_Replace gradleReplace = new Gradle_Replace(migrateTo34, migrateTo35, migrateToX);
        mGradleKeyList = gradleReplace.sGradleKeyList;
        mGradleValueList = gradleReplace.sGradleValueList;

        Gradle_ReplaceLine gradleReplaceLine = new Gradle_ReplaceLine(migrateTo34, migrateTo35, migrateToX, removeFacebook);
        mGradleKeyLineList = gradleReplaceLine.sGradleKeyLineList;
        mGradleValueLineList = gradleReplaceLine.sGradleValueLineList;

        IgnoredLine ignoredLine = new IgnoredLine(mIgnoredLine);
        mIgnoredKeyList = ignoredLine.sIgnoredKeyList;
        mIgnoredValueList = ignoredLine.sIgnoredValueList;

        JavaXml_Replace javaXmlReplace = new JavaXml_Replace(migrateTo34, migrateToX);
        mJXKeyList = javaXmlReplace.mJXKeyList;
        mJXValueList = javaXmlReplace.mJXValueList;

        JavaXml_ReplaceLine javaXmlReplaceLine = new JavaXml_ReplaceLine(migrateTo34, migrateToX, removeFacebook);
        mJXKeyLineList = javaXmlReplaceLine.mJXKeyLineList;
        mJXValueLineList = javaXmlReplaceLine.mJXValueLineList;
    }

    private boolean isTargetFile(String fileName) {
        String nameLowerCase = fileName.toLowerCase();
        for (String suffix : mNameSuffix) {
            boolean endsWith = nameLowerCase.endsWith(suffix);
            if (endsWith) {
                return true;
            }
        }
        return false;
    }

    private void printLogWithLineNum(String format, Object... args) {
        String msg = sF.format(format, args).toString();
        tmpSb.setLength(0);
        printLog(false, msg);
    }

    private void printLog(boolean error, String msg) {
        msg = error ? ("t-" + msg) : ("f-" + msg);
        mLogList.add(msg);
    }

    private void saveLogToFile() {
        for (String s : mLogList) {
            boolean error = s.startsWith("t-");
            String substring = s.substring(2);
            if (error) {
                System.err.println(substring);
            } else {
                System.out.println(substring);
            }

            mLogPr.print(substring);
            mLogPr.println();
        }
        mLogPr.flush();

        mLogList.clear();
    }

    public boolean copyFileByChannel(File sourceFilePath, File destinationFilePath) {
        FileChannel fSrc = null;
        FileChannel fDest = null;
        boolean ret;
        try {
            fSrc = new FileInputStream(sourceFilePath).getChannel();
            fDest = new FileOutputStream(destinationFilePath).getChannel();
            fDest.transferFrom(fSrc, 0, fSrc.size());
            ret = true;
        } catch (Throwable e) {
            ret = false;
            e.printStackTrace();
        } finally {
            closeIO(fSrc, fDest);
        }
        return ret;
    }

    private String getNameSpaceFromManifest(File file) {
        String prefix = "package=\"";
        BufferedReader br = null;
        try {
            br = new BufferedReader(new FileReader(file));
            String line;
            while ((line = br.readLine()) != null) {
                if (line.replace(" ", "").contains(prefix)) {
                    String[] splits = line.split(prefix);
                    String[] splitArr = splits[1].split("\"");
                    String str = splitArr[0];
                    return str;
                }
            }
        } catch (Throwable e) {
            e.printStackTrace();
        } finally {
            closeIO(br);
        }
        return "";
    }
//    private void readFile2KV(File file, ArrayList<String> keyList, ArrayList<String> valueList) {
//        System.out.println(file.getName() + " 开始读取------------");
//
//        String remPrefixStr = "@REM";/*注释：忽略这行不替换*/
//        String forceStopStr = "@END@END";/*强制结束读取*/
//        String splitStr = "======";/*分割 Key value*/
//
//        BufferedReader br = null;
//        try {
//            br = new BufferedReader(new FileReader(file));
//            String line;
//            while ((line = br.readLine()) != null) {
//                if ("".equals(line) || line.startsWith(remPrefixStr)) {
//                    continue;
//                }
//                if (line.contains(forceStopStr)) {
//                    System.out.println("------------已强制结束");
//                    break;
//                }
//                if (line.contains(splitStr)) {
//                    String[] split = line.split(splitStr);
//                    if (split.length == 1) {
//                        keyList.add(split[0]);
//                        valueList.add("");
//                    } else {
//                        keyList.add(split[0]);
//                        valueList.add(split[1].replace("\\\\", "\\"));
//                    }
//                } else {
//                    System.out.println("k-v 读取失败_：" + line);
//                }
//            }
//        } catch (Throwable e) {
//            e.printStackTrace();
//        } finally {
//            closeIO(br);
//        }
//    }

    private void closeIO(Closeable... cs) {
        for (Closeable c : cs) {
            if (c != null) {
                try {
                    c.close();
                } catch (Throwable e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
